package com.fingard.dsp.bank.directbank.citic04;

import com.fingard.constant.Format;
import com.fingard.dsp.bank.directConfig.ActSetItem;
import com.fingard.dsp.bank.directbank.DirectBase;
import com.fingard.dsp.bank.directbank.citic04.util.CommonUtils;
import com.fingard.dsp.bank.directbank.citic04.util.RSAUtil;
import com.fingard.net.ITcpClient;
import com.fingard.net.TcpReceiver;
import com.fingard.util.TransIDHelper;
import com.fingard.xml.XmlTextWriter;

import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.*;
import java.net.URL;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Date;


public class CITIC04Base extends DirectBase implements ITcpClient {
    protected String getCharset() {
        return getBankConfig().getCharset("GBK");
    }

    private static TransIDHelper idHelper = new TransIDHelper(1, 999);

    protected String getQryTransID() {
        Date tmpDateNow = new Date();
        return Format.DateTime12Year2Format.format(tmpDateNow) + idHelper.getNextIDStr();
    }

    //外层报文
    protected String writeCommenHeader(CITIC04Header bankHeader, ActSetItem actItem) throws Exception {
        XmlTextWriter xmlWriter = new XmlTextWriter();
        xmlWriter.writeStartDocByDeclaration("<?xml version=\"1.0\" encoding=\"" + getCharset() + "\"?>");
        xmlWriter.writeStartElement("ROOT");
        xmlWriter.writeElementString("msgno", bankHeader.msgno);//报文编号
        xmlWriter.writeElementString("version", "1.0");//版本号
        xmlWriter.writeElementString("trancode", bankHeader.tranCode);//交易码
        xmlWriter.writeElementString("sendNode", actItem.merchantId);//发送方节点号，使用由代付平台统一分配给各商户的商户号填充
        xmlWriter.writeElementString("receiveNode", bankHeader.receiveNode);//接收方节点号，分行节点号
        xmlWriter.writeElementString("sendDate", Format.Date8Format.format(new Date()));//请求日期（yyyyMMdd）
        xmlWriter.writeElementString("dateTime", Format.DateTime17Format.format(new Date()));//时间戳（yyyyMMddHHmmssSSS）
        xmlWriter.writeElementString("merNotifyUrl", "");
        xmlWriter.writeElementString("errCode", "");
        xmlWriter.writeElementString("errMsg", "");
        xmlWriter.writeEndElement();
        return xmlWriter.toXmlString();
    }

    public String[] sendToBank(String tmpReqStr, CITIC04Header bankHeader, ActSetItem actItem) throws Exception {
        String[] tmpRetStr = {"", ""};
        String[] message = encryptMessage(tmpReqStr, actItem);
        String encryptMessage = message[0];
        String signMessage = message[1];
        String outerMessage = writeCommenHeader(bankHeader, actItem);
        int indexIns = outerMessage.indexOf("</ROOT>");
        StringBuilder tmpSbPck = new StringBuilder();
        tmpSbPck.append(outerMessage.substring(0, indexIns));
        tmpSbPck.append("<encrypt>");
        tmpSbPck.append(encryptMessage);
        tmpSbPck.append("</encrypt>");
        tmpSbPck.append("<signature>");
        tmpSbPck.append(signMessage);
        tmpSbPck.append("</signature>");
        tmpSbPck.append(outerMessage.substring(indexIns));

        byte[] reqPostData = tmpSbPck.toString().getBytes();
        String serverIP;
        int serverPort = 443;
        String submitUrl = bankFront.serverURL;
        WriteBankLogLn2("提交银行的地址：" + submitUrl);
        URL u = new URL(submitUrl);
        serverIP = u.getHost();
        if (u.getPort() != -1) {
            serverPort = u.getPort();
        }
        WriteBankLogStep2(tmpSbPck);
        String trustStoreJksFileName = actItem.ownCertFilePath;
        System.setProperty("javax.net.ssl.trustStore", trustStoreJksFileName);
        System.setProperty("javax.net.ssl.trustStorePassword", "123456");
        System.setProperty("javax.net.ssl.trustStoreType", "JKS");
        // Getting the default SSL socket factory
        SSLSocketFactory f = (SSLSocketFactory) SSLSocketFactory.getDefault();
        System.out.println("The default SSL socket factory class: " + f.getClass());
        OutputStream os;
        byte[] respPostData = null;
        try {
            // Getting the default SSL socket factory
            SSLSocket c = (SSLSocket) f.createSocket(serverIP, serverPort);
            c.startHandshake();
            BufferedWriter w = new BufferedWriter(new OutputStreamWriter(c.getOutputStream()));
            BufferedReader r = new BufferedReader(new InputStreamReader(c.getInputStream()));
            os = c.getOutputStream();
            StringBuilder buffer = new StringBuilder();
            buffer.append("POST ").append(submitUrl).append(" HTTP/1.1\r\n");
            buffer.append("User-Agent: Java/1.6\r\n");
            buffer.append("Host: ").append(serverIP).append("\r\n");
            buffer.append("Content-Type: text/plain; charset=GBK\r\n");
            if (reqPostData == null || reqPostData.length == 0) {
                buffer.append("Content-Length: 0\r\n");
            } else {
                buffer.append("Content-Length: ").append(reqPostData.length).append("\r\n");
            }
            buffer.append("\r\n");
            System.out.println("--- Sending request headers:");
            CommonUtils.dump(">> ", buffer.toString());

            w.write(buffer.toString());
            w.flush();
            if (reqPostData != null && reqPostData.length > 0) {
                System.out.println("--- Sending request body data:");
                CommonUtils.dump(">> ", reqPostData);
                os.write(reqPostData);
                os.flush();
            }

            InputStream is = c.getInputStream();
            BufferedInputStream bis = new BufferedInputStream(is);

            String line;

            System.out.println("--- Receiving response:");

            int postDataLen = -1;
            while ((line = CommonUtils.streamReadLineDump(is)) != null) {
                if (line.length() == 0) {
                    break;
                }
                if (line.contains("Content-Length:")) {
                    postDataLen = new Integer(line.substring(line.indexOf("Content-Length:") + 16, line.length()));
                }
            }

            if (postDataLen > 0) {
                int totalBytesRead = 0;
                respPostData = new byte[postDataLen];
                while (totalBytesRead < postDataLen) {
                    int bytesRemaining = postDataLen - totalBytesRead;
                    int bytesRead = bis.read(respPostData, totalBytesRead, bytesRemaining);
                    if (bytesRead == -1) {
                        break; // socket has been closed
                    }
                    totalBytesRead += bytesRead;
                }
                if (totalBytesRead > 0) {
                    CommonUtils.dump("<< ", respPostData, 0, totalBytesRead);
                }
                if (totalBytesRead < postDataLen) {
                    System.out.println("读取信息不完整：totalBytesRead=" + totalBytesRead + ", postDataLen =" + postDataLen);
                } else {
                    System.out.println("recv resp:【" + new String(respPostData) + "】");
                }
            }
            w.close();
            r.close();
            c.close();
        } catch (IOException e) {
            tmpRetStr[0] = "-1";
            tmpRetStr[1] = e.getMessage();
            e.printStackTrace();
            WriteBankLogLn(e);
        }
        tmpRetStr[1] = new String(respPostData);
        return tmpRetStr;
    }

    //加密、签名
    protected String[] encryptMessage(String tmpReqStr, ActSetItem actItem) throws Exception {
        byte[] respMessage = tmpReqStr.getBytes(getCharset());
        String[] reqData = new String[2];
        try {
            FileInputStream priKeyStream = new FileInputStream(new File(actItem.ownKeyStorePath));
            RSAPrivateKey privateKey = RSAUtil.loadPrivateKey(priKeyStream);//己方私钥
            RSAPublicKey publicKey = RSAUtil.generatePublicKeyX509(actItem.oppCertFilePath);//对方公钥

            //加密 对方公钥 报文内容byte[]数组
            String encrypt = RSAUtil.encrypt(publicKey, respMessage);
            reqData[0] = encrypt;
            //签名 己方私钥 报文内容byte[]数组
            String signature = RSAUtil.sign(privateKey, respMessage);
            reqData[1] = signature;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return reqData;
    }

    //解密、验签
    protected String[] decryptMessage(String encrypt, String signature, ActSetItem actItem) throws Exception {
        byte[] decrypyBytes = null;
        String[] respData = new String[2];
        try {
            FileInputStream priKeyStream = new FileInputStream(new File(actItem.ownKeyStorePath));
            RSAPrivateKey privateKey = RSAUtil.loadPrivateKey(priKeyStream);//己方私钥
            RSAPublicKey publicKey = RSAUtil.generatePublicKeyX509(actItem.oppCertFilePath);//对方公钥

            //解密 己方私钥 密文
            decrypyBytes = RSAUtil.decrypt(privateKey, encrypt);

            //验签 对方公钥 签名值
            if (!RSAUtil.verify(publicKey, decrypyBytes, signature)) {
                respData[0] = "fail";
            }

            String strBytes = new String(decrypyBytes, getCharset());
            respData[1] = strBytes;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return respData;
    }

    @Override
    public void getContentLength(TcpReceiver p_receiver) throws Exception {
        if (p_receiver.contentLength == -1 && p_receiver.readedLength >= 10) {
            String tmpHead = new String(p_receiver.recBytes, 0, 10, getCharset());
            WriteBankLogStep3(tmpHead);
            p_receiver.contentLength = Integer.parseInt(tmpHead);
            p_receiver.contentStartIndex = 10;

        }
    }

}
